Introduction |
Aujourd'hui, l'informatique est présente dans
tous les domaines de la vie courante, mais à des degrés différents.
Il y a pour cela trois grandes raisons :
Deux domaines sont pleinement exploités :
- les logiciels généraux, vendus en grande série, et donc relativement bon marché, ( windows, word, ...)
- les logiciels spécifiques, d'un coût total important et donc limités à des sujets très pointus, pour de très grosses industries.(WVOffice de VIEWlogic@, gestion de site ou de process industriel )
Le domaine intermédiaire, qui
peut encore se développer, concerne les programmes spécifiques,
pour des applications de moindre importance. Pour cela, il est nécessaire
de disposer de langages de programmation. Les tableurs et bases de données
par exemple disposent désormais de véritables langages de
programmation (souvent orientés objets) qui vont plus loin que les
précédents langages de macro-commandes.
Pour les autres cas,
le C est
souvent le meilleur choix. En effet, c'est un langage structuré,
avec toutes les possibilités des autres langages structurés.
Mais il permet également (avec
son extension C++) de gérer des objets. A l'inverse, il permet
également une programmation proche du langage machine, ce qui est
nécessaire pour accéder aux interfaces entre l'ordinateur
et son extérieur. Mais son principal avantage est que ces trois
types de programmation peuvent être combinés dans un même
programme, tout en restant portable sur tous les ordinateurs existants.
Le langage C a néanmoins deux inconvénients
majeurs, c'est d'être un peu plus complexe d'utilisation (mais uniquement
du fait de ses nombreuses possibilités), et d'être séquentiel,
ce qui ne lui permettra pas d'être le langage optimal pour les machines
massivement parallèles (mais aujourd'hui il n'existe pas encore
de langage universel pour ce type de machines qui puisse combiner efficacement
des calculs procéduraux et du déclaratif).
Historique |
Langages de programmation |
Un ordinateur est une machine bête, ne sachant qu'obéir,
et à très peu de choses :
Sa puissance vient du fait qu'il peut être PROGRAMME, c'est à dire que l'on peut lui donner, à l'avance, la séquence (la suite ordonnée) des ordres à effectuer l'un après l'autre. Le grand avantage de l'ordinateur est sa rapidité. Par contre, c'est le programmeur qui doit TOUT faire. L'ordinateur ne comprenant que des ordres codés en binaire (le langage machine), des langages dits "évolués" ont été mis au point pour faciliter la programmation, au début des années 60, en particulier FORTRAN (FORmula TRANslator) pour le calcul scientifique et COBOL pour les applications de gestion. Puis, pour des besoins pédagogiques principalement, ont été créés le BASIC, pour une approche simple de la programmation, et PASCAL au début des années 70. Ce dernier (comme le C) favorise une approche méthodique et disciplinée (on dit "structurée"). Le C a été développé conjointement au système d'exploitation UNIX, dans les Laboratoires BELL, par Brian W Kernigham et Dennis M Ritchie, qui ont défini en 78, dans "The C Language", les règles de base de ce langage. Le but principal était de combiner une approche structurée (et donc une programmation facile) avec des possibilités proches de celles de l'assembleur (donc une efficacité maximale en exécution, quitte à passer plus de temps de programmation), tout en restant standard (c'est à dire pouvoir être implanté sur n'importe quelle machine). Puis ce langage a été normalisé en 1988 (norme ANSI), cette norme apportant un nombre non négligeable de modifications au langage.
Le C est un langage compilé, c'est à dire qu'il faut :
* entrer un texte dans l'ordinateur (à l'aide d'un programme appelé EDITEUR),
* le traduire en langage machine (c'est à dire en codes binaires compréhensibles par l'ordinateur) : c'est la compilation et, si plusieurs modules ont été compilés séparément, l'édition de liens (LINK ou BIND),
* l'exécuter.
Contrairement à un basic interprété, l'exécution sera beaucoup plus rapide puisqu'il n'y a plus de traduction à effectuer, mais la phase de mise au point sera plus complexe.
Bien que le langage soit normalisé,
un certain nombre de points dépendent de la machine et du compilateur
utilisé (par exemple comment appeler le compilateur). Ces indications
ne seront pas données ici. Si vous avez le choix, je vous conseille
TURBO
C, le plus pratique d'emploi (en particulier parce qu'il possède
son propre éditeur de texte). Il permet une mise au point aussi
simple que si le langage était interprété.
Caractéristiques |
Langage structuré,
conçu pour traiter les tâches d'un programme en les mettant
dans des blocs.
Il produit des programmes efficaces
: il possède les mêmes possibilités de contrôle
de la machine que l'assembleur et il
génére un code compact et
rapide.
Déclaratif : normalement,
tout objet C doit être déclaré avant d'être utilisé.
S'il ne l'est pas, il est considéré comme étant
du type entier.
Format libre : la mise en page
des divers composants d'un programme est totalement libre.
Cette possibilité doit être exploitée pour rendre les programmes lisibles.
Modulaire : une application pourra être découpée en modules qui pourront être compilés séparément.
Un ensemble de programmes déjà
opérationnels pourra être réuni dans une librairie.
Cette aptitude permet au langage C
de se développer de lui même.
Souple et permissivité
: peu de vérifications et d'interdits, hormis la syntaxe. Il est
important de remarquer que la
tentation est grande d'utiliser cette caractéristique
pour écrire le plus souvent des atrocités.
Transportable : les entrées/sorties
sont réunies dans une librairie externe au langage.
Structure d'un programme C |
Un programme C est composé de :
Le préprocesseur (/lib/cpp) est appelé automatiquement, en tout premier, par la commande /bin/cc.
On ne doit pas les imbriquer et ils peuvent apparaître en tout point d'un programme (sauf dans une constante de type chaîne de caractères ou caractère).
Pour ignorer une partie de programme il est préférable d'utiliser une directive du préprocesseur (#if 0 ... #endif)
Connaissances de base |
#include <stdio.h>
#define TVA 18.6 void main(void) { float HT,TTC; puts ("veuillez entrer le prix H.T."); scanf("%f",&HT); TTC=HT*(1+(TVA/100)); printf("prix T.T.C. %f\n",TTC); } |
On trouve dans ce programme :
* des directives du pré processeur (commençant par #) ( pas de ; )
* une entête de fonction. Dans ce cas on ne possède qu'une seule fonction, la fonction principale (main function). Cette ligne est obligatoire en C, elle définit le "point d'entrée" du programme, c'est à dire l'endroit où débutera l'exécution du programme. Les fonctions sont écrites sous la forme : entête { corps }#include : inclure le fichier définissant (on préfère dire déclarant) les fonctions standard d'entrées/sorties (en anglais STanDard In/Out), qui feront le lien entre le programme et la console (clavier/écran). Dans cet exemple il s'agit de puts, scanf et printf.#define : définit une constante. A chaque fois que le compilateur rencontrera, dans sa traduction de la suite du fichier en langage machine, le mot TVA, ces trois lettres seront remplacées par 18.6. Ces transformation sont faites dans une première passe (appelée pré compilation), où l'on ne fait que du "traitement de texte", c'est à dire des remplacements d'un texte par un autre sans chercher à en comprendre la signification.
L'entête est de la forme : type_résultat nom (arguments) . Le type_résultat n'était obligatoire (avant la norme ANSI) que s'il était différent de int (entier). Il doit désormais être void (rien) si la fonction ne renvoie rien (dans un autre langage on l'aurait alors appelé sous-programme, procédure, ou sous-routine). Les arguments, s'ils existent, sont passés par valeur. Si la fonction ne nécessite aucun argument, il faut indiquer (void) d'après la norme ANSI, ou du moins (),
Le corps est composé de déclarations de variables locales, et d'instructions, toutes terminées par un ;
* un "bloc d'instructions", délimité par des accolades { }, et comportant :
* des déclarations de variables, sous la forme : type listevariables;Un bloc est une suite d'instructions élémentaires délimitées par des accolades { et }Une variable est un case mémoire de l'ordinateur, que l'on se réserve pour notre programme. On définit le nom que l'on choisit pour chaque variable, ainsi que son type, ici float, c'est à dire réel (type dit à virgule flottante, d'où ce nom). Les trois types scalaires de base du C sont l'entier (int), le réel (float) et le caractère (char). On ne peut jamais utiliser de variable sans l'avoir déclarée auparavant. Une faute de frappe devrait donc être facilement détectée, à condition d'avoir choisi des noms de variables suffisamment différents (et de plus d'une lettre).
* des instructions, toutes terminées par un ; (point virgule). Une instruction est un ordre élémentaire que l'on donne à la machine, qui manipulera les données (variables) du programme, ici soit par appel de fonctions (puts, scanf, printf) soit par affectation (=).
L'instruction nulle est composée d'un ; seul.
Il est recommandé, afin de faciliter la lecture et le "deboguage" de ne mettre qu'une seule instruction par ligne dans le source du programme.
Un bloc peut contenir un ou plusieurs blocs inclus
Un bloc peut commencer par des déclarations/définitions d'objets qui seront locaux à ce bloc. Ces objets ne pourront être utilisés que dans ce bloc et les éventuels blocs inclus à ce bloc.
Détaillons les 4 instructions de notre programme
:
puts affiche à l'écran le texte qu'on lui donne (entre parenthèses, comme tout ce que l'on donne à une fonction, et entre guillemets, comme toute constante texte en C).
scanf attend que l'on entre une valeur au clavier, puis la met dans la mémoire (on préfère dire variable) HT, sous format réel (%f).
une affectation : on commence par diviser TVA par 100 (à cause des parenthèses), puis on y ajoute 1, puis on le multiplie par le contenu de la variable HT. Le résultat de ce calcul est stocké (affecté) dans la variable cible TTC. Une affectation se fait toujours dans le même sens : on détermine (évalue) tout d'abord la valeur à droite du signe =, en faisant tous les calculs nécessaires, puis elle est transférée dans la mémoire dont le nom est indiqué à gauche du =. On peut donc placer une expression complexe à droite du =, mais à sa gauche seul un nom de variable est possible, aucune opération.
printf affichera enfin le résultat
stocké dans TTC.
Fonctions d'entrées/sorties les plus utilisées |
Le langage C se veut totalement
indépendant du matériel sur lequel il est implanté.
Les entrées sorties, bien que nécessaires à tout programme
(il faut lui donner les données de départ et connaître
les résultats), ne font donc pas partie intégrante du langage.
On a simplement prévu des bibliothèques
de fonctions de base, qui sont néanmoins standardisées, c'est
à dire que sur chaque compilateur on dispose de ces bibliothèques,
contenant les même fonctions (du moins du même nom et faisant
apparemment la même chose, mais programmées différemment
en fonction du matériel). Ces bibliothèques ont été
définies par la norme ANSI, on peut donc les utiliser tout
en restant portable. Nous détaillerons ici deux bibliothèques
: CONIO.H et STDIO.H.
Dans CONIO.H on trouve les fonctions de base de gestion de la console :
putch(char) : affiche sur l'écran (ou du moins stdout) le caractère fourni en argument (entre parenthèses). stdout est l'écran, ou un fichier si on a redirigé l'écran (en rajoutant >nomfichier derrière l'appel du programme, sous DOS ou UNIX). Si besoin est, cette fonction rend le caractère affiché ou EOF en cas d'erreur.
getch(void) : attend le prochain appui sur le clavier, et rend le caractère qui a été saisi. L'appui sur une touche se fait sans écho, c'est à dire que rien n'est affiché à l'écran. En cas de redirection du clavier, on prend le prochain caractère dans le fichier d'entrée.
getche(void) : idem getch mais avec
écho
Dans STDIO.H, on trouve des fonctions plus évoluées, pouvant traiter plusieurs caractères à la suite (par les fonctions de conio.h), et les transformer pour en faire une chaîne de caractères ou une valeur numérique, entière ou réelle par exemple. Les entrées sont dites "bufférisées", c'est à dire que le texte n'est pas transmis, et peut donc encore être modifié, avant le retour chariot.
On possède encore d'autres fonctions dans STDIO, en particulier
pour gérer les fichiers.
Structure d'une fonction |
Une fonction est un bloc de code d'une ou plusieurs instructions qui peut renvoyer une valeur à l'expression qui l'utilise.
Elle peut retourner une valeur à la
fonction appelante.
Le programme principal est une fonction dont
le nom doit impérativement être main.
Les fonctions ne peuvent pas être imbriquées.
La forme générale (en ANSI)d'une fonction est :
[classe] [type] nom( [liste_de_parametres_formels] )
bloc_de_la_fonction
Les éléments entre [] dans cette syntaxe, signifique que cet élément est facultatif, car une valeur par défaut existe.
En C, le programme principal et les sous-programmes sont définis comme fonctions. Il n'existe pas de structures spéciales pour le programme principal ni les procédures (comme en Pascal ou en langage algorithmique).
Le programme principal étant aussi une 'fonction', nous devons nous intéresser dès le début à la définition et aux caractéristiques des fonctions en C. Commençons par comparer la syntaxe de la définition d'une fonction en C avec celle d'une fonction en langage algorithmique:
Définition d'une fonction en langage algorithmique
fonction <NomFonct> (<NomPar1>, <NomPar2>, ...):<TypeRés> | <déclarations des paramètres> | <déclarations locales> | <instructions> ffonction
Définition d'une fonction en C
|<TypeRés> <NomFonct> (<TypePar1> <NomPar1>, | <TypePar2> <NomPar2>, ... ) |{ | <déclarations locales> | <instructions> |}En C, une fonction est définie par:
* une ligne déclarative qui contient:
<TypeRés> - le type du résultat de la fonction | |
<NomFonct> - le nom de la fonction | |
<TypePar1> <NomPar1>, <TypePar2> <NomPar2>, ... | |
les types et les noms des paramètres de la fonction |
* un bloc d'instructions délimité par des accolades {
}, contenant:
<déclarations locales> - les déclarations des données locales (c.-à-d.: des données qui sont uniquement connues à l'intérieur de la fonction) | |
<instructions> - la liste des instructions qui définit l'action qui doit être exécutée |
Résultat d'une fonction
Par définition, toute fonction en C fournit un résultat dont le type doit être défini. Si aucun type n'est défini explicitement, C suppose par défaut que le type du résultat est int (integer).
Le retour du résultat se fait en général à la fin de la fonction par l'instruction return.
Le type d'une fonction qui ne fournit pas de résultat (comme
les procédures en langage algorithmique ou en Pascal), est déclaré
comme void (vide).
Paramètres d'une fonction
La définition des paramètres (arguments) d'une fonction est placée entre parenthèses ( ) derrière le nom de la fonction. Si une fonction n'a pas besoin de paramètres, les parenthèses restent vides ou contiennent le mot void. La fonction minimale qui ne fait rien et qui ne fournit aucun résultat est alors:
void dummy() {}
Instructions
En C, toute instruction simple est terminée par un point-virgule ; (même si elle se trouve en dernière position dans un bloc d'instructions). Par exemple:
printf("hello, world\n");
Règles d'écriture des programmes C |
Afin d'écrire des programmes C lisibles, il est important de respecter un certain nombre de règles de présentation :
Exemple de programme |
1ère ligne
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34ème ligne |
/* **************************
Programme de démonstration ***************************/ #include <stdio.h> /* directives au préprocesseur */
main()
/* debut programme principal */
printf(MSG);
}
/* fin du bloc for */
int cube(int x) { /* definition de la fonction cube */
int carre(int x) { /* definition de la fonction carre */
|
Identificateurs |
Les identificateurs nomment les objets C (fonctions, variables ... )
auto | double | int | struct |
break | else | long | switch |
case | enum | register | typedef |
char | extern | return | union |
const | float | short | unsigned |
continue | for | signed | void |
default | goto | sizeof | volatile |
do | if | static | while |
Variables / identificateurs / adresse / pointeurs |
On appelle variable une mémoire de l'ordinateur (ou plusieurs), à laquelle on a donné un nom, ainsi qu'un type (on a précisé ce qu'on mettra dans cette variable (entier, réel, caractère,...), pour que le compilateur puisse lui réserver la quantité de mémoire nécessaire. Dans cette variable, on pourra y stocker une valeur, et la modifier au cours du programme.
Exemple : int a; a est une variable entière, le compilateur va lui réserver en mémoire la place nécessaire à un entier (2 octets en Turbo C). Le nom de cette variable est choisi par le programmeur. On préfère utiliser le terme identificateur plutôt que le nom, car il permet d'identifier tout objet que l'on voudra utiliser (pas seulement les variables).
Les identificateurs doivent suivre quelques règles de base : il peut être formé de lettres (A à Z), de chiffres et du caractère _ (souligné). Le premier caractère doit être une lettre (ou _ mais il vaut mieux le réserver au compilateur). Par exemple valeur1 ou prem_valeur sont possibles, mais pas 1ere_valeur. En C, les minuscules sont différentes des majuscules (SURFace et surFACE désignent deux objets différents). Le blanc est donc interdit dans un identificateur(utilisez _). Les lettres accentuées sont également interdites. La plupart des compilateurs acceptent n'importe quelle longueur d'identificateurs (tout en restant sur la même ligne) mais seuls les 32 premiers caractères sont significatifs.
On considère comme blanc : soit un blanc (espace), soit un retour à la ligne, soit une tabulation, soit un commentaire, soit plusieurs de ceux-ci. Les commentaires sont une portion de texte commençant par /* et finissant par le premier */ rencontré. les commentaires ne peuvent donc pas être imbriqués. mais un commentaire peut comporter n'importe quel autre texte, y compris sur plusieurs lignes.
Un identificateur se termine soit par un blanc, soit par un signe non autorisé dans les identificateurs (parenthèse, opérateur, ; ...). Le blanc est alors autorisé mais non obligatoire.
L'endroit où le compilateur a choisi de mettre la variable
est appelé adresse de la variable (c'est
en général un nombre, chaque mémoire d'un ordinateur
étant numérotée de 0 à ? ). Cette adresse ne
nous intéresse que rarement de manière explicite, mais souvent
de manière indirecte. Par exemple, dans un tableau, composé
d'éléments consécutifs en mémoire, en connaissant
son adresse (son début), on retrouve facilement l'adresse des différentes
composantes par une simple addition.
On appelle pointeur
une variable dans laquelle on place (mémorise) une adresse de variable
(où elle est) plutôt qu'une valeur (ce qu'elle vaut).
Les types de variables scalaires simples que l'on utilise le plus
couramment sont le char (un caractère), l'int (entier)
et le float (réel). Le char
est en fait un cas particulier des int, chaque caractère étant
représenté par son numéro de code ASCII.
Expressions / opérateurs |
Une expression est un calcul qui donne une valeur résultat (exemple : 8+5). Une expression comporte des variables, des appels de fonction et des constantes combinés entre eux par des opérateurs (ex : MaVariable*sin(VarAngle*PI/180) ).
Une expression de base peut donc être un appel
à une fonction (exemple sin(3.1416) ).
Une fonction est un bout de programme
(que vous avez écrit ou faisant partie d'une bibliothèque)
auquel on "donne" des valeurs (arguments), entre
parenthèses et séparés par des virgules. La fonction
fait un calcul sur ces arguments pour "retourner" un résultat. Ce
résultat pourra servir, si nécessaire, dans une autre expression,
voire comme argument d'une fonction exemple atan(tan(x)).
Les arguments donnés à l'appel de la fonction
(dits paramètres réels ou effectifs)
sont recopiés dans le même ordre dans des copies (paramètres
formels), qui elles ne pourront que modifier les copies (et pas les paramètres
réels).
Dans le cas de fonctions devant
modifier une variable, il faut fournir en argument l'adresse
(par l'opérateur &,
voir plus bas), comme par exemple pour scanf.
Pour former une expression, les opérateurs
possibles sont assez nombreux, nous allons les détailler suivant
les types de variables qu'ils gèrent.
Séquences d'échappement: voir les deux bibliothèques : CONIO.H et STDIO.H. |
Comme nous l'avons vu au chapitre 2, l'impression et l'affichage de texte peut être contrôlé à l'aide de séquences d'échappement. Une séquence d'échappement est un couple de symboles dont le premier est le signe d'échappement '\'. Au moment de la compilation, chaque séquence d'échappement est traduite en un caractère de contrôle dans le code de la machine. Comme les séquences d'échappement sont identiques sur toutes les machines, elles nous permettent d'écrire des programmes portables, c.-à-d.: des programmes qui ont le même effet sur toutes les machines, indépendemment du code de caractères utilisé.
Séquences d'échappement
\a | sonnerie | \\ | trait oblique |
\b | curseur arrière | \? | point d'interrogation |
\t | tabulation | \' | apostrophe |
\n | nouvelle ligne | \" | guillemets |
\r | retour au début de ligne | \f | saut de page (imprimante) |
\0 | NUL | \v | tabulateur vertical |
Le caractère NUL
La constante '\0' qui a la valeur numérique zéro (ne pas à confondre avec le caractère '0' !!) a une signification spéciale dans le traitement et la mémorisation des chaînes de caractères: En C le caractère '\0' définit la fin d'une chaîne de caractères.
1) caractères précédés de " % " :
. %b : affiche en binaire
. %c : affiche un caractère
. %d : affiche en décimal
. %o : affiche en octal
. %s : affiche une chaine
. %u : affiche en décimal non signé
. %x : affiche en hexadécimal
2) caractères de commande précédés
de " \" :
. \a : cloche (bel $07 )
. \? : point d'interrogation
. \b : tabulation ( $08 )
. \f : saut de page ( $0c )
. \n : à la ligne ( $0a )
. \r : retour chariot ( $0d )
. \' : apostrophe ( ' )
. \" : guillemets ( "
)
. \xHH : valeur en hexadécimal ( 2 chiffres )
Exemple printf("n =
%d soit en hexa %x et en binaire %b",x,x,x);